package GA;



import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.text.SimpleDateFormat;
import java.io.*;
import java.util.Date;

import Operations.ValuesGenerator;
import util.*;
import random.TestClusterBuilder;
import instrumentation.TrackerCoverage;
public class GeneticAlgorithm {
	protected static Logger logger = Logger.getLogger("Genetic Algorithm class.");	
	private GAInfo     gaInfo     = new GAInfo();
	private TestClusterBuilder TestCluster;
	private  TrackerCoverage track=  TrackerCoverage.getTracker();

	private  long startTime;
	private  long elapsed;
	 private static  long TestTimeout; //  60 * 500  Timeout at 30 seconds */

	private void initialize() throws ClassNotFoundException{
		//Read the input file
		System.out.println("CUT is:" + GAConfig.classFilePath);
		 Set<Class<?>> allClasses = new LinkedHashSet<Class<?>>();
	      //get class from file
	      allClasses.addAll(TestClusterBuilder.readClassFromFile());

	      System.out.println("There are " + allClasses.size() + " classes as input");
	      
	      System.out.println(allClasses);
	      Set<Class<?>> classesToTest = TestClusterBuilder.filterUntestableClasses(allClasses);
	      System.out.println("There are " + classesToTest.size() + " classes to test after filtering");
	      System.out.println(classesToTest);

		// calculate the test cluster and the set of types involved in the
		 TestCluster = new TestClusterBuilder(classesToTest);		
		//Generate random population
		Chromosome[] population = Population.randomPopulationInitializer(TestCluster);
		
		gaInfo.setPopulation(population);
		
		this.gaInfo.setGeneration(0);
		this.gaInfo.setPopulationFitness(0);
	}
	private void execute() {
		//Calculate fitness of initial population

		System.out.println("\t **** execute generation "+ this.gaInfo.getGeneration() +" elapsed time: "+ elapsed + " of "+TestTimeout);

		Chromosome[] population=this.gaInfo.getPopulation();
		if (GAConfig.PrintPopulation) System.out.println(Population.toString(gaInfo));
		float populationFitness=0;
		for(int i=0; i<population.length && elapsed < TestTimeout; i++) {
			Chromosome individual=population[i];
//			System.out.println("GeneticAlgorithm.execute : "+individual.toString());
			ReflectionExecutor.execute(individual);
			float individualFitness= track.calculateFitness(individual);
			populationFitness+=individualFitness;
			individual.setFitness(individualFitness);
			
			//System.out.println("\tfitness= "+individualFitness);
			track.reset();
			elapsed = (Calendar.getInstance().getTimeInMillis() - startTime);
		}
		 ArrayList<Chromosome> l = new ArrayList<Chromosome>(Arrays.asList(track.branchCoveredByIndividual));
		 l.removeAll(Collections.singleton(null)); 
		System.out.println("\t Total Branch Covered= ( "+  l.size()  +" )");

		if (GAConfig.PrintPopulation) System.out.println(Population.toString(gaInfo));
		this.gaInfo.setPopulationFitness(l.size());
		this.gaInfo.setLastBestFitness(this.gaInfo.getPopulationFitness());

	}
	private void evolve(){

		for ( int it = 1; (it<=500000 && elapsed < TestTimeout) ; it++) {
			Chromosome[] population = Population.generateNewPopulation(this.gaInfo,TestCluster);
			
		
			gaInfo.setPopulation(population);
			this.gaInfo.setGeneration(this.gaInfo.getGeneration() + 1);	
			this.execute();
			
			if(this.gaInfo.getLastBestFitness() >= this.gaInfo.getPopulation()[0].getFitness())
				this.gaInfo.setNoImprovementCount(this.gaInfo.getNoImprovementCount() + 1);
			else
				this.gaInfo.setNoImprovementCount(0);
			
//		    if (GAConfig.SaveData) Log.log("No Improvement Count:" + this.gaInfo.getNoImprovementCount()+ "\n" );
			this.gaInfo.setLastBestFitness(this.gaInfo.getPopulation()[0].getFitness());

			elapsed = (Calendar.getInstance().getTimeInMillis() - startTime);

		}


	}
	public void start() {
		try {
			startTime = Calendar.getInstance().getTimeInMillis();
			elapsed = Calendar.getInstance().getTimeInMillis() - startTime;

			this.initialize();
			//Calculate fitness of initial population
			 this.execute();
			//iteratively recombined and mutated to evolve successive populations
			 this.evolve();
			 System.out.println ("Done testing at generation: " + gaInfo.getGeneration());
			 System.out.println("Maximum test time exceeded at " + elapsed );			 
			 

			 if (GAConfig.SaveData) Log.log ("Done testing at generation: " + gaInfo.getGeneration());
			 if (GAConfig.SaveData) Log.log("Maximum test time exceeded at " + elapsed );
			
	//		 track.summarizeCoverage("Genetic Algorithm");
		
			System.out.println(track.toString());
			if (GAConfig.SaveData) 
				Log.log(track.toString());
		 
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Something went wrong: " + e.getMessage(), e);
		}
	
		
	}
	public static void writeLogFile(int i ){
		String logFilePath = null;
		String fileName = new SimpleDateFormat("MM_dd_yyyy_hhmm'.txt'")
			.format(new Date());
		
	String cName = GAConfig.classFilePath.substring(0,GAConfig.classFilePath.lastIndexOf("/") );
	cName = cName.substring(cName.lastIndexOf("/") +1);
	//cName=GAConfig.classFilePath;

		logFilePath = "./logFiles/"+ cName+ "_GA_"+i+ "_N_" + GAConfig.N+ "_"+ fileName;
		
	if (logFilePath != null) {
		Log.logConfig(logFilePath);
//		experiment_configure_options(TestTimeout);
		System.out.println("Log file place: "+ new File(logFilePath).getAbsolutePath());
		}
		Log.log("       SUT               : "+ GAConfig.classFilePath);
		Log.log ("      Population Size   : "+GAConfig.PopulationSize);
		Log.log ("      TestCase Size     : "+GAConfig.TestCaseSize);
		Log.log ("      Elitism       : "+GAConfig.elitismNumBest);
		Log.log ("      N   			: "+GAConfig.N);
		Log.log ("      Timeout           : "+TestTimeout/60000 + " minutes  " + TestTimeout+" ms");
		Log.log ("");

	}
	private static void experiment_configure_options(long TestTimeout,int j) {
		Log.log("Start testing #..."+j);
		Log.log ("      Seed              : "+randomseed);
	}
	//the random seed
	public static long randomseed;// = (int) GAConfig.RANDOM_SEED;
	private void setTestTimeout(int i){
		if(i!=0)
		 TestTimeout = 60 * 1000*i;
		else
			 TestTimeout = 60 * 500;
			
		 
	}
	public static void main(String [] args) throws IOException{
		for(int i=0;i<=0;i+=6){
			GeneticAlgorithm algorithm = new GeneticAlgorithm();
			
			algorithm.setTestTimeout(i);
			
			if (GAConfig.SaveData)
				writeLogFile(i);
		     //set the random generation seeds

		      for (int j = 1; j <= 1; j += 1) {
		    	  System.out.println("Start testing #..."+j);
					//set the random generation seeds
			    	  randomseed= System.currentTimeMillis();
//		    	 randomseed=1380266417599L;
			   	  ValuesGenerator.reset(randomseed);

			   	  if (GAConfig.SaveData)
					experiment_configure_options(TestTimeout,j);

				algorithm.start();

				algorithm.track.setTrackerToNull();
				algorithm = new GeneticAlgorithm();

			}
				if (GAConfig.SaveData)
					Log.log.close();
		}

		}

    
	
}
